#include <iostream>
#include <string>
using namespace std;

//类和对象 封装 继承 多态

const float PI = 3.14f;

class Circle
{
public:

    int r;

    float calculateZC()
    {
        return 2 * PI * r;
    }

};

void test01()
{
    Circle c1;
    c1.r = 10;
    cout << "circle zc = " << c1.calculateZC() << endl;
}


//学生案例

class Student
{
public:
    string name;
    int id;

    void showInfo()
    {
        cout << "name: " << name << ";" << "id:" << id << endl;
    }

    void setName(string name)
    {
        this->name = name;
    }

    void setId(int id)
    {
        this->id = id;
    }
};

void test11()
{
    Student s1;
    s1.name = "Jack";
    s1.id = 233;
    s1.showInfo();

    Student s2;
    s2.setName("Rose");
    s2.setId(244);
    s2.showInfo();
}

//类的访问权限
//public 类内类外都可访问 继承时属性不变
//protected 类内访问 继承时成员变成protected属性 子类类内访问
//private 类内访问 继承时子类不可访问 但是继承到了子类中


class Person
{
public:
    string name;
protected:
    string car;
private:
    int password;

public:
    void setInfo(string name, string car, int password)
    {
        this->name = name;
        this->car = car;
        this->password = password;
    }
    void showInfo()
    {
        cout << name << car << password << endl;//类内均可以访问
    }

};

void test21()
{
    Person p1;
    p1.name;//类外访问
    // p1.car;//类外不可访问
    // p1.password;//类外不可访问
    p1.setInfo("Jack", "BMW" ,123456);
    p1.showInfo();//通过public接口可以访问所有属性
}

//struct class 区别
//struct 默认权限是公共的 class 默认权限是私有的


//权限控制

class C1
{
private:
    string name;//可读可写
    int age;//只读
    string idol;//只写 对于写接口 可以设置条件对属性进行写入控制

//通过函数接口控制权限
public:
    void setName(string name)
    {
        this->name = name;
    }
    void showName()
    {
        cout << name << endl;
    }

    void showAge()
    {
        cout << age <<endl;
    }

    void setIdol(string idol)
    {
        this->idol = idol;
    }

};


//构造函数和析构函数
//构造可以有参数 可重载 析构函数没有参数 不可重载

class C2
{
public:
    C2()//C2的无参构造
    {
        cout << "C2() call" << endl;
    }

    ~C2()
    {
        cout << "~C2() call" << endl;
    }
};

void test30()
{
    C2 c;//自动调用无参构造 函数退出时销毁对象会自动调用析构函数
}

//构造函数的分类和调用
//有参 无参构造
//普通 拷贝构造

//调用方式 括号法 显示法 隐式转换法

class C4
{
public:
    C4()
    {
        cout << "C4() call" <<endl;
    }

    C4(int age)
    {
        this->age = age;
        cout << "C4(int age) call" <<endl;
    }
    
    C4(const C4 &c) //拷贝构造 默认这种写法
    {
        this->age = c.age;
        cout << "C4(const C4 &c) call" << endl;
    }

    ~C4()
    {
        cout << "~C4() call" <<endl;
    }

    int age;

};

void test40()
{
    //括号法
    // C4 c;//无参 默认构造调用
    // C4 c1(10);//有参构造调用
    // C4 c2(c1);//拷贝构造调用

    //显示法
    // C4 c3 = C4(10);//有参构造 等号右边其实是一个匿名对象 左边给匿名对象取名 
    // C4 c4 = C4(c3);//拷贝构造
    // C4(10);//匿名对象特点：当前行结束掉，系统会自动回收匿名对象空间

    //隐式转换法
    C4 c5 = 10;//相当于C4 c5 = C4(10) 有参构造
    C4 c6 = c5;//拷贝构造
}

//拷贝构造函数调用时机
//使用一个已将创建完毕的对象来初始化一个对象
//值传递的方式给函数参数传值 实参传递给形参时
//以值的方式返回局部对象


class Person5
{
public:
    Person5()
    {
        cout << "Person5() call" <<endl;
    }

    Person5(int age)
    {
        this->age = age;
        cout << "Person5(int age) call" <<endl;
    }
    
    Person5(const Person5 &p) //拷贝构造 默认这种写法
    {
        this->age = p.age;
        cout << "Person5(const Person5 &c) call" << endl;
    }

    ~Person5()
    {
        cout << "~Person5() call" <<endl;
    }

    int age;

};

void doWork50(Person5 p)
{
    cout << "doWork50() call" << endl;
}

Person5 doWork51()
{
    Person5 p;
    return p;
}


void test50()
{
    Person5 p1(10);
    Person5 p2(p1);

    doWork50(p2);//值传递方式 这个过程是将实参拷贝给形参的过程 拷贝构造 即 Person5 p = p2

    //g++ -fno-elide-constructors  .\test02.cpp 这里编译防止被优化 看不到拷贝构造
    Person5 p3 = doWork51();//以值的方式接收返回值 是将局部变量拷贝出来 即 Person5 p3 = p;
}


//构造函数的调用规则
//默认情况下 c++编译器至少给一个类添加3个函数
//1、无参构造 函数体为空
//2、析构函数 函数体为空
//3、默认拷贝构造 对属性进行值拷贝 或者浅拷贝

//函数的调用规则如下
//如果用户定义有参构造 编译器不提供无参构造 但会提供拷贝构造
//如果用户提供拷贝构造 编译器不提供其他构造函数


//深拷贝和浅拷贝
class Person6
{
public:
    Person6()
    {
        cout << "Person6() call" << endl;
    }

    Person6(int age, int height)
    {
        this->age = age;
        this->height = new int(height);
        cout << "Person6(int, int*) call" << endl;
    }

    Person6(const Person6 &p)
    {
        this->age = p.age;
        this->height = new int(*p.height);
    }

    ~Person6()//浅拷贝会重复释放同一片堆区空间 解决这个问题就是要自己写一个拷贝构造 实现深拷贝
    {
        if(height != NULL)
        {
            delete height;
            height = NULL;
        }
    }
    int age;
    int *height;
};

void test60()
{
    Person6 p1(25, 170);
    Person6 p2(p1);//编译器默认提供的拷贝构造是值拷贝 需要自己实现深拷贝构造
    cout << p1.height << endl;//两个地址一致
    cout << p2.height << endl;
}

//初始化列表

class Person7
{
public:
    //Person7():age(10),name("Jack"),id(233){}//初始化列表 这种写法写死了 按照以下方法写
    Person7(int a, string b, int c):age(a),name(b),id(c){}

    int age;
    string name;
    int id;
};

void test70()
{
    Person7 p1(25,"TOM",233);
    cout << p1.age << endl;
    cout << p1.name << endl;
    cout << p1.id << endl;

}


//静态成员

//在成员前加上 static 关键词修饰
//静态成员也有访问权限 protected private的属性类外无法访问


//静态成员变量
//1、所有对象共享一份数据
//2、在编译阶段分配内存
//3、类内声明 类外初始化 

class Person8
{
public:
    static int age;
};

int Person8::age = 20;//类内声明 类外初始化

void test80()
{
    Person8 p1;
    cout << p1.age << endl;

    Person8 p2;
    p2.age = 30;
    cout << p1.age << endl;//共享一个属性 变成30

    //静态属性共享数据 因此可以通过对象访问 也可以通过类名访问
    cout << Person8::age << endl;
}

//静态成员函数
//1、所有对象共享同一个函数
//2、静态成员函数只能访问静态成员变量  因为只有一份函数 无法识别哪一个对象

class Person9
{
public:
    static void fun()
    {
        age = 80;//访问静态变量 无问题
        //id = 233;//无法访问非静态对象 因为所有对象共享一份静态函数 静态函数无法区分特定的对象属性
        cout << "this is static fun() call" <<endl;
    }

    static int age;
    int id;
};

int Person9::age = 90;

void test90()
{
    Person9 p1;
    p1.fun();//对象访问
    Person9::fun();//类名访问
}


//c++对象模型和this指针
//在c++中 类的成员变量和成员函数分开存储
//只有非静态成员变量才属于类的对象上

class Person10
{
public:
    Person10(int age)
    {
        this->age = age;
    }

    Person10 & addPerson(Person10 p)//实现链式编程 必须返回引用 否则返回拷贝的副本
    {
        this->age += p.age;
        return *this;
    }

    int age;
};

void test101()
{
    Person10 p1(10);
    Person10 p2(10);
    p2.addPerson(p1).addPerson(p1).addPerson(p1);
    cout << "p2.age = " << p2.age <<endl;

}

//const修饰成员函数 常函数
//1、常函数不可以修改成员属性
//2、成员属性声明是加关键字 mutable 后 在常函数中可以修改

//const修饰的对象称为常对象
//常对象只能调用常函数

class Person11
{
public:
    Person11()
    {
        age = 20;
        id = 233;
    }
    //this指针本质是一个指针常量 指向被调用的函数的对象 加上const修饰后 变成常量指针常量 不能修改指向的对象
    //这里const修饰的是this指针
    void showPerson() const
    {
        //this->age = 20;//报错 因为常函数不能修改成员属性
        this->id = 233;//mutable修饰的属性 可以通过常函数修改
    }

    void fun(){}

    int age;
    mutable int id;
};

void test110()
{
    const Person11 p1;//常对象 必须要初始化 因为不能修改 自然后续也无法赋值了
    //p1.age = 30;//报错 常对象属性无法修改
    p1.id = 366;//特殊属性 可以修改
    p1.showPerson();//常对象可以调用常函数
    //p1.fun();//报错 常对象不能调用非常函数 因为非常函数可能修改对象的属性
}


//友元 让一个函数或者类 访问另一个类中的私有成员
//简而言之：如果想访问一个类中的私有属性 这个函数、或者成员函数、或者这个类需要是其友元

//关键字 friend

//友元的3种实现
//1、全局函数作友元
//2、类做友元
//3、成员函数作友元


//全局函数作友元
class Building12
{
    friend void goodFriend(Building12 *b);
public:
    Building12()
    {
        SittingRoom = "keting";
        BeddingRomg = "woshi";
    }

public:
    string SittingRoom;
private:
    string BeddingRomg;
};

void notFriend(Building12 *b)
{
    cout << "notFriend visit " << b->SittingRoom <<endl;
    //cout << "visit" << b->BeddingRomg << endl;//非友元 不能访问
}

void goodFriend(Building12 *b)
{
    cout << "goodFriend visit " << b->SittingRoom <<endl;
    cout << "goodFriend visit " << b->BeddingRomg << endl;//友元 能访问
}

void test120()
{
    Building12 b;
    notFriend(&b);
    goodFriend(&b);
}

//类作友元

class Building13
{
    friend class GoodFriend13;//GoodFriend类是Building13类的友元 可以访问其私有属性
public:
    Building13()
    {
        SittingRoom = "keting";
        BeddingRomg = "woshi";
    }

public:
    string SittingRoom;
private:
    string BeddingRomg;
};

class GoodFriend13
{
public:
    GoodFriend13()
    {
        building = new(Building13);
    }
    void visit()
    {
        cout << "visit " << building->SittingRoom << endl;
        cout << "visit " << building->BeddingRomg << endl;//BeddingRomg是私有属性 若想访问 需要将GoodFriend设置为其友元
    }

    Building13 *building;
};

void test130()
{
    GoodFriend13 gf;
    gf.visit();
}


//成员函数作友元
//成员函数的声明必须要在之前 否则不生效

//https://blog.csdn.net/TheThree_body/article/details/123917930 参考解释

class Building14;
class GoodFriend14
{
public:
    GoodFriend14();//这里不写构造 是因为不知道Building14长啥样

    void visit1();//非友元 不可访问
    void visit2();//友元 可以访问 这里不写实现是不知道访问的内容有哪些 因为Building14类只有声明 还不知道具体内容

    Building14 *building;
};

//Building14 的类要在友元成员函数类之后 不然不知道类是否有成员函数
class Building14
{
    friend void GoodFriend14::visit2();
public:
    Building14();

public:
    string SittingRoom;
private:
    string BeddingRoom;
};


Building14::Building14()
{
    SittingRoom = "keting";
    BeddingRoom = "woshi";
}

GoodFriend14::GoodFriend14()
{
    building = new Building14;
}

void GoodFriend14::visit1()
{
    cout << building->SittingRoom << endl;
    //cout << building->BeddingRoom << endl;//报错 非友元函数不能访问私有属性
}

void GoodFriend14::visit2()
{
    cout << building->SittingRoom << endl;
    cout << building->BeddingRoom << endl;//报错 非友元函数不能访问私有属性
}

void test140()
{
    GoodFriend14 gf;
    gf.visit1();
    gf.visit2();
}


//运算符重载
//加号运算符重载
//1、成员函数重载加号
//2、全局函数重载加号

class Person15
{
public:
    // Person15 operator+(Person15 &p)
    // {
    //     Person15 temp;
    //     temp.a = a + p.a;
    //     temp.b = b + p.b;
    //     return temp;
    // }
    int a;
    int b;
};

//全局函数重载
Person15 operator+(Person15 &p1, Person15 &p2)
{
    Person15 temp;
    temp.a = p1.a + p2.a;
    temp.b = p1.b + p2.b;
    return temp; 
}

//运算符重载 也可以发生函数重载
Person15 operator+(Person15 &p1, int num)
{
    Person15 temp;
    temp.a = p1.a + num;
    temp.b = p1.b + num;
    return temp; 
}


void test150()
{
    Person15 p1;
    p1.a = 10;
    p1.b = 20;
    Person15 p2;
    p2.a = 20;
    p2.b = 10;
    //成员函数重载 Person15 p3 = p1.operator+(p2)
    //全局函数重载 Person15 p3 = operator+(p1, p2)
    Person15 p3 = p1 + p2;
    cout << p3.a << " " << p3.b << endl;

    p3 = p1 + 100;
    cout << p3.a << " " << p3.b << endl;
}

//左移运算符重载
class Person16
{
public:
    //想要实现 cout << p 的自定义打印 如果用成员函数实现 调用是 p.operator<<(cout) 效果是 p << cout 无法实现cout在左侧
    //因此需要通过全局函数实现
    // void operator<<(cout)
    // {

    // }
    int a;
    int b;
};

ostream & operator<<(ostream &cout, Person16 &p) //本质是operator<<(cout, p) 实际是 cout << p;
{
    cout << p.a << ";" << p.b;
    return cout;
}

void test160()
{
    Person16 p1;
    p1.a = 10;
    p1.b = 100;
    cout << p1 << endl;//返回cout之后 才能继续追加endl;
}

//递增运算符重载
class MyInteger17
{
    friend ostream & operator<<(ostream &cout, MyInteger17 mt);
public:
    MyInteger17()
    {
        num = 0;
    }

    //前置++ 的重载
    MyInteger17 & operator++() // ++mt mt.operator++()
    {
        ++num;
        return *this;//需要配合cout重载输出时 需要返回对象 需要实现连续++时 需要返回引用
    }

    //后置++重载 int占位参数 用于区分前置和后置递增
    //后置递增是先返回++前状态 再对对象进行运算 无法对同一个对象连续++
    MyInteger17 operator++(int) // mt++ mt.operator++()
    {
        MyInteger17 temp = *this;
        ++num;
        return temp;//注意 这里不能返回局部对象的引用 局部对象再出函数时就销毁了
    }   

private:
    int num;
};

ostream & operator<<(ostream &cout, MyInteger17 mt)
{
    cout << mt.num;
    return cout;
}

void test170()
{
    MyInteger17 mt;
    cout << mt << endl;
    cout << ++mt << endl;
    cout << ++(++mt) << endl;//前置++可以连续加
    cout << mt << endl;
    cout << mt++ << endl;
    cout << mt << endl;
}

//赋值运算符重载
//编译器也会默认添加赋值运算符重载 同样也会出现浅拷贝问题

class Person18
{
public:
    Person18(int age)
    {
        this->age = new int(age);
    }

    int *age;
};

class Person181//重载等号运算符版本
{
public:
    Person181(int age)
    {
        this->age = new int(age);
    }

    Person181 & operator=(Person181 &p)//返回引用是为了实现连等的操作 类似于 a = b = c
    {
        if(age)
        {
            delete age;
            age = new int(*p.age);
            return *this;
        }
        this->age = new int(*p.age);
        return *this;
    }

    ~Person181()
    {
        if(age)
        {
            delete age;
            age = NULL;
        }
    }

    int *age;
};

void test180()
{
    Person18 p1(18);
    Person18 p2(28);
    p2 = p1;//编译器默认提供了operator=()的重载 但是只是简单的值拷贝
    cout << p1.age << endl;
    cout << p2.age << endl;//两个地址一样 同样引发浅拷贝的问题 如果想要避免 重载等号运算符

    Person181 p3(18);
    Person181 p4(28);
    Person181 p5(38);
    p5 = p3 = p4;
    cout << p3.age << " " << *p3.age << endl;
    cout << p4.age << " " << *p4.age << endl;
    cout << p5.age << " " << *p3.age << endl;
}


//关系运算符重载
class Person19
{
public:
    Person19(string name, int age)
    {
        this->age = age;
        this->name = name;
    }

    bool operator==(Person19 &p)//p1 == p2 p1.operator==(p2)
    {
        if(age == p.age && name == p.name)
        {
            return true;
        }
        return false;
    }

    bool operator!=(Person19 &p)//p1 != p2 p1.operator!=(p2)
    {
        if(age == p.age && name == p.name)
        {
            return false;
        }
        return true;
    }
    int age;
    string name;
};

void test190()
{
    Person19 p1("tom", 19);
    Person19 p2("tom", 19);
    if(p1 == p2)
    {
        cout << "p1 == p2" << endl;
    }
    else
    {
        cout << "p1 != p2" << endl;
    }

    Person19 p3("tom", 19);
    Person19 p4("jerry", 19);

    if(p3 == p4)
    {
        cout << "p3 == p4" << endl;
    }
    else
    {
        cout << "p3 != p4" << endl;
    }

}


//函数调用运算符()重载 由于重载后的使用方式非常像函数的调用 又称为仿函数

//实现一个打印的类  mp("hello") mp.operator()("hello")

class MyPrint20
{
public:
    void operator()(string str)
    {
        cout << str << endl;
    }
};


class MyAdd
{
public:
    int operator()(int num1, int num2)
    {
        return num1 + num2;
    }
};

void test200()
{
    MyPrint20 mp;
    mp("hello!");//使用起来非常像函数调用 仿函数

    MyAdd add;
    int sum = add(10, 20);
    cout << sum << endl;
}

//继承
//继承语法 class 子类 : 继承方式 父类

//继承方式 父类的private属性 虽然继承到了子类中 但是子类是访问不到的（只是被隐藏了）
//公共继承 父类的属性在子类中保持不变
//保护继承 父类的属性在子类中变为保护属性
//私有继承 父类的属性在子类中变为私有属性

class Base21
{
public:
    int a;
protected:
    int b;
private:
    int c;
};

class Son1 : public Base21
{
public:
    void fun()
    {
        a = 10;//public
        b = 20;//protected
        //c = 30;//报错 继承的父类的私有属性 无法访问
    }
};

class Son2 : protected Base21
{
public:
    void fun()
    {
        a = 10;//protected
        b = 20;//protected
        //c = 30;//报错 继承的父类的私有属性 无法访问
    }
};

class Son3 : private Base21
{
public:
    void fun()
    {
        a = 10;//private
        b = 20;//private
        //c = 30;//报错 继承的父类的私有属性 无法访问
    }
};

class GrandSon3 : public Son3
{
public:
    void fun()
    {
        //a = 10;//报错 继承的父类的私有属性 无法访问
        //b = 20;//报错 继承的父类的私有属性 无法访问
        //c = 30;//在Son3中就无法访问
    }
};

//继承中的构造和析构顺序
class Base22
{
public:
    Base22()
    {
        cout << "Base22() call" << endl;
    }
    ~Base22()
    {
        cout << "~Base22() call" << endl;
    }
};


class Son22 : public Base22
{
public:
    Son22()
    {
        cout << "Son22() call" << endl;
    }
    ~Son22()
    {
        cout << "~Son22() call" << endl;
    }
};

void test220()
{
    //先构造父类 再构造子类
    //先析构子类 再析构父类
    Son22 s1;
}

//继承中同名成员的处理方式
//访问子类同名成员时 直接访问
//访问父类同名成员时 加上作用域

class Base23
{
public:
    Base23()
    {
        same = 100;
    }

    void fun()
    {
        cout << "Base23-fun()" <<endl;
    }

    void fun(int a)
    {
        cout << "Base23-fun(int a)" << endl;
    }

    int same;
};

class Son23 : public Base23
{
public:
    Son23()
    {
        same = 200;
    }

    void fun()//子类中其实有两个fun()成员
    {
        cout << "Son23-fun()" << endl;
    }

    int same;//此时子类其实有两份same属性
};

void test230()
{
    //同名成员属性的处理方式
    Son23 s1;
    cout << "son : s1.same = " << s1.same << endl;
    cout << "Base23 : s1.Base23::same = " << s1.Base23::same << endl;

    //同名成员函数的处理方式
    s1.fun();
    s1.Base23::fun();

    //注意：下方调用会报错 如果子类中出现和父类同名的成员函数 子类会隐藏父类所有的同名成员函数
    //s1.fun(10);
    s1.Base23::fun(20);//尽管子类没有fun(int a)的函数 也需要加上作用域
}

//继承中同名静态成员处理方式
class Base24
{
public:
    static void fun()//只能访问静态成员
    {
        cout << "Base24-fun()" <<endl;
    }
    static int a;//类内声明 类外初始化
};

int Base24::a = 100;

class Son24 : public Base24
{
public:
    static void fun()//只能访问静态成员
    {
        cout << "Son24-fun()" <<endl;
    }
    static int a;//同名静态成员
};

int Son24::a = 200;

void test240()
{
    //通过对象访问呢
    Son24 s;
    cout << "Son24: a = " << s.a << endl;
    cout << "Base24: a = " << s.Base24::a <<endl;

    //通过类名访问
    cout << "Son24: a = " << Son24::a <<endl;
    cout << "Base24: a = " << Base24::a <<endl;
    cout << "Base24: a = " << Son24::Base24::a <<endl;//子类作用域中的 父类中的静态成员a

    s.fun();//对象访问
    Son24::fun();//类名访问
    s.Base24::fun();
    Base24::fun();
    Son24::Base24::fun();
    //最后注意 与之前一样 如果子类中有同名的静态成员函数 会隐藏所有父类的同名静态成员函数 此时必须加上作用域才能访问到父类的静态成员函数
}


//多继承语法
//class A : public Base1, protected Base2 {};

class Base251
{
public:
    Base251()
    {
        a = 20;
    }
    int a;
};


class Base252
{
public:
    Base252()
    {
        b = 30;
    }
    int b;
};

class Son25 : public Base251, public Base252
{
public:
    Son25()
    {
        c = 40;
        d = 50;
    }
    int c;
    int d;
};


//多态

//静态多态 动态多态
//静态多态 函数重载 和 运算符重载
//动态多态 派生类 和 虚函数 运行时多态

//静态多态和动态多态的区别
//静态多态 函数地址早绑定 编译阶段确定函数地址
//动态多态 函数地址晚绑定 运行阶段确定函数地址


//动态多态的满足条件
//1、有继承关系
//2、子类要重写父类的虚函数 重写不是重载 是和父类完全相同的函数
//3、父类中的函数加virtual关键词 子类重写的函数可加可不加

//动态多态的使用
//父类的指针或者引用 执行子类对象 如下 可以通过传入不同的对象 执行不同对象中的函数
// void doSpeak_v(Animal26 &animal)
// {
//     animal.speak_v();
// }

class Animal26
{
public:
    void speak()//这中写法无法实现动态多态
    {
        cout << "Animal speaks" << endl;
    }

    virtual void speak_v()//动态多态写法 virtual 关键词
    {
        cout << "Animal speaks_v" << endl;
    }
};

class Cat26 : public Animal26
{
public:
    void speak()
    {
        cout << "Cat speaks" << endl;
    }
    void speak_v()
    {
        cout << "Cat speaks_v" << endl;
    }
};

//这种写法是地址早绑定 在编译阶段就确定了函数地址 稳定调用Animal26类中的speak() 所以无论传入什么参数 都是不变的
void doSpeak(Animal26 &animal)
{
    animal.speak();
}

void doSpeak_v(Animal26 &animal)
{
    animal.speak_v();
}

void test260()
{
    Animal26 animal;
    doSpeak(animal);
    Cat26 cat;
    doSpeak(cat);//Animal26 &animal = cat c++中允许父子类的自动类型转换

    doSpeak_v(animal);
    doSpeak_v(cat);
}

//多态的原理
//父类中的虚函数 会变成维护一个虚函数指针 当子类继承这个虚函数指针并且不重写时 指向的还是父类的虚函数 重写时指向子类自己的虚函数

//虚函数和虚继承
//虚其实就是 不是真正的意思 当子类没有同类的成员 继承父类 子类有了 覆盖父类 实现了多态 也可以解决菱形继承的问题（重复成员问题）

//多态案例

//在真实的开发中 开闭原则 对扩展开放 对修改封闭

class AbstractCal
{
public:
    virtual int getResult()
    {
        return 0;
    }

    int num1;
    int num2;
};

class Add : public AbstractCal
{
public:
    int getResult()
    {
        return num1 + num2;
    }
};

class Sub : public AbstractCal
{
public:
    int getResult()
    {
        return num1 - num2;
    }
};

//按照上述思路 扩展运算非常方便

int calFun(AbstractCal &as)
{
    return as.getResult();
}

void test270()
{
    Add add;
    add.num1 = 20;
    add.num2 = 20;
    cout << calFun(add) << endl;

    Sub sub;
    sub.num1 = 20;
    sub.num2 = 10;
    cout << calFun(sub) << endl;
}

//纯虚函数和抽象类
//多态中 父类中的虚函数实现是毫无意义的 主要调用的是子类重写的内容
//因此可以将虚函数改为纯虚函数
//语法 virtual int fun(int a) = 0;
//当类中有了纯虚函数 这个类称为抽象类
//抽象类无法实例化对象 子类也必须重写抽象类中的纯虚函数 否则也属于抽象类

//虚析构和纯虚析构
//多态使用时 如果子类中有属性开辟到堆区 那么父类指针在释放时无法调用到子类的析构代码
//解决方式 将父类中的析构函数改为虚析构或者纯虚析构 其实可以理解为析构的多态

//虚析构语法 ~类名(){}
//纯虚析构语法 ~类名() = 0;

class Animal28
{
public:
    Animal28()
    {
        cout << "Animal28()" << endl;
    }
    // virtual ~Animal28()//虚析构 为了保证在多条情况下 父类的析构同样能调用到子类的析构
    // {
    //     cout << "~Animal28()" << endl;
    // }

    virtual ~Animal28() = 0;//如果写成纯虚析构 编译器强制要求有实现 类外实现 防止父类空间释放不干净
    virtual void speak() = 0;
};

Animal28::~Animal28()
{
    cout << "Animal28() = 0" << endl;
}

class Cat28 : public Animal28
{
public:
    Cat28(string kind)
    {
        cout << "Cat28(string kind)" << endl;
        this->kind = new string(kind);
    }
    void speak()
    {
        cout << "Cat speaks" <<endl;
    }
    ~Cat28()
    {
        cout << "~Cat28()" << endl;
        if(kind)
        {
            delete kind;
            kind = NULL;
        }
    }

    string *kind;
};

void test280()
{
    // Cat28 cat("daju");
    // Animal28 *al = &cat;
    // al->speak();//这种方式都能调用到父类和子类的析构

    Animal28 *al = new Cat28("daju");//这种匿名没法调用子类的析构
    al->speak();//多态使用
    delete al;//多态父类指针的析构 不会调用到子类的析构函数
}

int main()
{
    //test01();

    //test11();

    //test21();

    //构造和析构
    //test30();

    //构造函数的分类和调用
    // test40();

    //拷贝构造的调用时机
    //test50();

    //深拷贝和浅拷贝
    //test60();

    //初始化列表
    //test70();


    //静态属性
    //test80();

    //静态成员函数
    //test90();

    //this指针用法
    //test101();

    //常函数
    //test110();

    //友元
    //test120();
    //test130();
    //test140();

    //加法运算符重载
    //test150();

    //左移运算符重载
    //test160();

    //递增运算符重载
    //test170();

    //赋值运算符重载
    //test180();

    //关系运算符重载
    //test190();

    //函数调用运算符重载
    //test200();

    //继承中的构造和析构顺序
    //test220();

    //继承中同名成员的处理方式
    //test230();

    //继承中同名静态成员的处理方式
    //test240();


    //多态
    //test260();

    //多态案例
    //test270();

    //多态的析构
    test280();

    return 0;
}